from __future__ import annotations

import json
import logging
from datetime import datetime, timedelta, timezone
from typing import Any, Dict

from sqlmodel import Session, select

from ..db import engine
from ..models import Setting, SessionLog

logger = logging.getLogger(__name__)


DEFAULTS: Dict[str, Any] = {
    "log_level": "INFO",
    "ssh_timeout_seconds": 10,
    "ssh_keepalive_seconds": 30,
    "ws_heartbeat_seconds": 20,
    "log_flush_threshold_bytes": 16 * 1024,
    "log_retention_days": 30,
}


def _get_raw_map(session: Session) -> Dict[str, str]:
    rows = session.exec(select(Setting)).all()
    return {r.key: r.value for r in rows}


def read_settings() -> Dict[str, Any]:
    with Session(engine) as session:
        raw = _get_raw_map(session)
    result: Dict[str, Any] = {}
    for k, v in DEFAULTS.items():
        if k not in raw:
            result[k] = v
        else:
            try:
                result[k] = json.loads(raw[k])
            except Exception:  # noqa: BLE001
                result[k] = raw[k]
    return result


def write_settings(patch: Dict[str, Any]) -> Dict[str, Any]:
    with Session(engine) as session:
        for k, v in patch.items():
            if k not in DEFAULTS:
                continue
            session.merge(Setting(key=k, value=json.dumps(v)))
        session.commit()
    return read_settings()


def cleanup_logs_if_needed() -> int:
    """Delete old logs beyond retention; return deleted rows count."""
    cfg = read_settings()
    days = int(cfg.get("log_retention_days", DEFAULTS["log_retention_days"]))
    cutoff = datetime.now(timezone.utc) - timedelta(days=days)
    deleted = 0
    with Session(engine) as session:
        # 手动删除（SQLite 不支持 RETURNING 简便计数）
        rows = session.exec(select(SessionLog).where(SessionLog.started_at < cutoff)).all()
        for r in rows:
            session.delete(r)
            deleted += 1
        session.commit()
    if deleted:
        logger.info("[SETTINGS] cleaned %s old logs (retention_days=%s)", deleted, days)
    return deleted

